﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Sockets;

namespace BerkelyAlgorithem2
{
    public partial class Form1 : Form
    {
        private tcpServer ser;
        private tcpClient cli;
        Form2 f;
        private NeighbourTable ntb;
        //private NeighbourTable ntbLocal;
        private System.Threading.Thread[] electionListeningThreads;
        Boolean electionsStarted;
        ElectionProccess electionManager;
        SlaveForm s;
        MasterForm m;

        public Form1()
        {
            InitializeComponent();
            //add call to tcp listen
            label2.Text = "Wait for input";
            electionsStarted = false;

        }

        ~Form1()
        {

            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        public void selectionFinished(NeighbourTable n)
        {
            this.ntb = n;
            this.button1.Visible = false;
            this.button1.Enabled = false;
            int offset = 0;

            //init and send neighbourtable to all
            foreach (Neighbour ne in ntb.Neighbours)
            {
                Console.WriteLine("ne: " + ne.Address.ToString() + " tcpu: " + utils.FindLanAddress().ToString());

                offset++;
                string os = offsetNeighbour(offset);

                if (!ne.Address.ToString().Equals(utils.FindLanAddress().ToString()))
                {
                    cli = new tcpClient(ne.Address, ne.Port);
                    cli.sendMsg(os);
                    cli.exit();
                }
                //else
                //{
                //    ntbLocal = new NeighbourTable(utils.StrToByteArray(os));
                //    Console.WriteLine("ntbLocal: " + ntbLocal.NeighbourAsString);
                //}

            }


            button1.Visible = false;
            button2.Visible = false;
            label2.Text = "Connecting...";

            if (f != null)
            {
                f.Close();
                f.Dispose();
            }
            openForms();


        }

        private string offsetNeighbour(int k)
        {
            int len = ntb.Neighbours.Count;
            string res = "";
            foreach (Neighbour n in ntb.Neighbours)
            {
                if (k > len)
                    k = 1;
                int outPort = n.Port + k;
                res += n.Address + ":" + outPort + ";";
                k++;
            }
            return res;
        }

        private void listenForElections(NeighbourTable ntb)
        {
            electionListeningThreads = new System.Threading.Thread[ntb.Neighbours.Count - 1];

            int i = 0;

            try
            {

                foreach (Neighbour n in ntb.Neighbours)
                {
                    if (!n.Address.ToString().Equals(utils.FindLanAddress().ToString()))
                    {
                        electionListeningThreads[i] = new System.Threading.Thread(() => listen_For_Elections_That_I_Didnt_Start(n));
                        electionListeningThreads[i].IsBackground = true;
                        electionListeningThreads[i].Start();

                        i++;
                    }
                }
            }
            catch (ElectionStartedException e)
            {
                ElectionProccess electionManager = new ElectionProccess(ntb, electionListeningThreads);
                if (long.Parse(e.UUID) > electionManager.UUID)
                {
                    //i lost

                    //restart the reporting thread (the one who generated the exception)
                    i = 0;
                    foreach (System.Threading.Thread t in electionListeningThreads)
                    {
                        if (!t.IsAlive)
                        {
                            electionListeningThreads[i] = new System.Threading.Thread(() => listen_For_Elections_That_I_Didnt_Start(e.NeighbourPointer));
                        }
                        i++;
                    }

                    //keep listening for further instructions

                }
                else
                {
                    //summon a 'iAmAlive+UUID' function
                    electionManager.broadCast("IAmAlive;" + electionManager.UUID);

                    //keep listening (maybe someone is bigger)
                    i = 0;
                    foreach (System.Threading.Thread t in electionListeningThreads)
                    {
                        if (!t.IsAlive)
                        {
                            electionListeningThreads[i] = new System.Threading.Thread(() => listen_For_Elections_That_I_Didnt_Start(e.NeighbourPointer));
                        }
                        i++;
                    }

                }

                //wait for someone to win
            }
            catch (SomeOneWonException s)
            {
                ElectionProccess electionManager = new ElectionProccess(ntb, electionListeningThreads);

                //update ntb with the winner



                //goto ClockSync (where i am a slave)

            }

        }

        public void listen_For_Elections_That_I_Didnt_Start(Neighbour n)
        {
            //save the threads id for later use
            //n.threadID = System.Threading.Thread.CurrentThread.ManagedThreadId;

            tcpServer ser = new tcpServer(IPAddress.Any, n.Port);

            while (true)
            {
                byte[] msg = ser.getMsg();

                string str = utils.ByteToStrArray(msg);

                if (str.Contains("ElectionsStarted")) //someone started elections
                {
                    if (!electionsStarted)
                    {
                        electionsStarted = true;
                        ser.exit();

                        //delete this if needed
                        System.Threading.Thread.Sleep(100);

                        throw new ElectionStartedException(str.Split(';')[1], n);
                    }
                }
                else if (str.Contains("IWon")) //someone won
                {

                }
            }
        }



        private void button2_Click_1(object sender, EventArgs e)
        {
            f = new Form2(this);
        }

        private void button1_Click_1(object sender, EventArgs e)
        {
            label2.Text = "Listening...";
            button1.Enabled = false;
            button2.Enabled = false;
            ser = new tcpServer(IPAddress.Any, 8001);

            //get table
            byte[] msg = ser.getMsg();

            ntb = new NeighbourTable(msg);
            Console.WriteLine(ntb.NeighbourAsString);
            button1.Visible = false;
            button2.Visible = false;

            openForms();
        }

        private void openForms()
        {
            electionManager = new ElectionProccess(ntb);
            bool amIMaster = electionManager.start();

            if (amIMaster)
            {
                System.Threading.Thread.Sleep(1000);
                m = new MasterForm(this.ntb, this);
                m.ShowDialog();

            }
            else
            {
                try
                {

                    s = new SlaveForm(this.ntb, this);
                    s.ShowDialog();


                }
                catch (ElectionStartedException e)
                {
                    //the old king is dead
                    e.NeighbourPointer.IsAlive = false;

                    //long live the new king!
                    electionManager.start();
                }
            }
        }

        public void electionsStartedExceptionWasThrown(Neighbour ne, SlaveForm s)
        {
            electionManager = new ElectionProccess(ntb);
            s.Close();
            //the old king is dead
            foreach (Neighbour n in ntb.Neighbours)
            {
                if (ne.Address.ToString().Equals(n.Address.ToString()))
                    n.IsAlive = false;
            }
            //electionManager.start();
            //long live the new king!
            openForms();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //ElectionProccess electionManager = new ElectionProccess(ntb);
            //bool amIMaster=electionManager.start();

            //if (amIMaster)
            //{
            //    MasterForm m = new MasterForm(this.ntb);
            //    m.ShowDialog();
            //    this.Close();
            //}
            //else
            //{
            //    SlaveForm s = new SlaveForm(this.ntb);
            //    s.ShowDialog();
            //    this.Close();
            //}
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);

            if (e.CloseReason == CloseReason.WindowsShutDown) return;

            // Confirm user wants to close
            switch (MessageBox.Show(this, "Are you sure you want to close?", "Closing", MessageBoxButtons.YesNo))
            {
                case DialogResult.No:
                    e.Cancel = true;
                    break;
                default:
                    break;
            }
        }

    }
}
